package be.kuleuven.cs.mop.domain.model.impl;


import static org.junit.Assert.*;

import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import org.junit.*;

import be.kuleuven.cs.mop.TestEnvironment;
import be.kuleuven.cs.mop.domain.exceptions.BusinessRuleTwoViolationExceptionDependantTasks;
import be.kuleuven.cs.mop.domain.exceptions.TaskManagerException;
import be.kuleuven.cs.mop.domain.model.Task;
import be.kuleuven.cs.mop.domain.model.TaskStatus;



public class TaskStateTest {
	
	private static TestEnvironment ENV;
	
	private static TaskState failed, succesful, unfinished;
	
	
	
	@Before
	public void setUp() throws Exception {
		ENV = TestEnvironment.newInstance();
		
		failed		= new TaskStateFailed(ENV.task1);
		succesful	= new TaskStateSuccessful(ENV.task1);
		unfinished	= new TaskStateUnfinished(ENV.task1);
	}
	
	@BeforeClass
	public static void setUpBeforeClass() throws Exception {}
	
	@After
	public void tearDown() throws Exception {}
	
	@AfterClass
	public static void tearDownAfterClass() throws Exception {}
	
	
	
	@Test(expected=NullPointerException.class)
	public void testConstructorInvalidNull() {
		new TaskStateUnfinished(null);
	}
	
	@Test
	public void testConstructorsValid() {
		new TaskStateFailed(ENV.task1);
		new TaskStateSuccessful(ENV.task1);
		new TaskStateUnfinished(ENV.task1);
	}
	
	@Test
	public void testEnforceBusinessRules() {
		ENV.world.getClock().setTime(ENV.datum2.getTimeInMillis() + 1);
		
//		failed.enforceBusinessRule2();
		failed.enforceBusinessRule3(ENV.world.getClock());
//		failed.enforceBusinessRules();
		
//		succesful.enforceBusinessRule2();
		succesful.enforceBusinessRule3(ENV.world.getClock());
//		succesful.enforceBusinessRules();
		
//		unfinished.enforceBusinessRule2();
		unfinished.enforceBusinessRule3(ENV.world.getClock());
//		unfinished.enforceBusinessRules();
	}
	
	@Test
	public void testFlags() {
		assertFalse(failed.isAvailable());
		assertFalse(succesful.isAvailable());
//		assertTrue(unfinished.isAvailable());
		
		assertTrue(failed.isFailed());
		assertFalse(succesful.isFailed());
		assertFalse(unfinished.isFailed());
		
		assertFalse(failed.isModifiable());
		assertFalse(succesful.isModifiable());
		assertTrue(unfinished.isModifiable());
		
		assertFalse(failed.isSuccessful());
		assertTrue(succesful.isSuccessful());
		assertFalse(unfinished.isSuccessful());
		
		assertFalse(failed.isUnfinished());
		assertFalse(succesful.isUnfinished());
		assertTrue(unfinished.isUnfinished());
	}
	
	@Test
	public void testSet() {
		final TaskImpl	task	= ENV.task1;
		final Schedule	sched	= task.getSchedule();
		final Calendar
		end		= new GregorianCalendar(),
		start	= new GregorianCalendar();
		final long time = ENV.world.getClock().getTime().getTimeInMillis();
		end.setTimeInMillis(time + 60000);
		start.setTimeInMillis(time - 60000);
		
		try {
			task.modify(
					task.getFields(),
					new Schedule(start, end, sched.getDuration()),
					task.getDependencies());
			
			failed.setFailedSafely();
			failed.setSuccessfulSafely();
			failed.setUnfinishedSafely();
			succesful.setFailedSafely();
			succesful.setSuccessfulSafely();
			succesful.setUnfinishedSafely();
			unfinished.setFailedSafely();
			unfinished.setSuccessfulSafely();
			unfinished.setUnfinishedSafely();
		} catch (final Exception e) {
			e.printStackTrace();
			assertFalse(true);
		}
	}
	
	@Test
	public void testSetRecursively() throws TaskManagerException {
		final Set<TaskImpl> dependencies = new HashSet<TaskImpl>();
		
		final TaskImpl	taskA	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		dependencies.clear();
		dependencies.add(taskA);
		final TaskImpl	taskB	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		dependencies.clear();
		dependencies.add(taskB);
		final TaskImpl	taskC	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		final Task[] tasks = {
				taskA,
				taskB,
				taskC
		};
		
		taskA.updateTaskStatusRecursively(TaskStatus.FAILED);
		for (final Task task : tasks)
			if (!task.getStatus().equals(TaskStatus.FAILED))
				assertFalse(true);
	}
	
	@Test
	public void testSetViolatingBusinessRuleTwoDependantTasks()
			throws TaskManagerException {
		final Set<TaskImpl> dependencies = new HashSet<TaskImpl>();
		
		final TaskImpl	taskA	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		dependencies.clear();
		dependencies.add(taskA);
		final TaskImpl	taskB	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		dependencies.clear();
		dependencies.add(taskB);
		final TaskImpl	taskC	= ENV.user1.createTask(
				ENV.schedule1,
				dependencies,
				new HashMap<String, Field>(),
				ENV.testTaskType);
		
		try {
			taskA.updateTaskStatus(TaskStatus.FAILED);
			assertFalse(true);	//	exception is requierd
		} catch (final BusinessRuleTwoViolationExceptionDependantTasks e) {
			//	expected
			final Map<? extends Task, TaskStatus> suggestions = e.getSuggestions();
			for (final Entry<? extends Task, TaskStatus> entry : suggestions.entrySet()) {
				final Task task = entry.getKey();
				if (task.equals(taskA)) {
					//	independant task should not get suggested status
					assertFalse(true);
				} else if (task.equals(taskB) || task.equals(taskC)) {
//					assertTrue(true);
				} else {
					//	horrible error
					assertFalse(true);
				}
			}
		} catch (final Exception e) {
			e.printStackTrace();
			assertFalse(true);
		}
	}
	
	@Test
	public void testValidation() {
		assertNotNull(failed.toStatus());
		assertEquals(failed.toStatus(),			TaskStatus.FAILED);
		assertNotNull(succesful.toStatus());
		assertEquals(succesful.toStatus(),		TaskStatus.SUCCESSFUL);
		assertNotNull(unfinished.toStatus());
		assertEquals(unfinished.toStatus(),		TaskStatus.UNFINISHED);
		
		assertNotNull(failed.toString());
		assertFalse(failed.toString().isEmpty());
		assertNotNull(succesful.toString());
		assertFalse(succesful.toString().isEmpty());
		assertNotNull(unfinished.toString());
		assertFalse(unfinished.toString().isEmpty());
	}
	
}